iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0
自我挑戰組

自學vue~點亮Roadmap過程系列 第 28

vue3鍊成術第二十八天-模板引用

  • 分享至 

  • xImage
  •  

模板引用

雖然 Vue 的聲明性渲染模型為你抽象了大部分對 DOM 的直接操作,但在某些情況下,我們仍然需要直接訪問底層 DOM 元素。要實現這一點,我們可以使用特殊的 ref attribute:

<input ref="input">

ref 是一個特殊的 attribute,和 v-for 章節中提到的 key 類似。它允許我們在一個特定的 DOM 元素或子組件實例被掛載後,獲得對它的直接引用。這可能很有用,例如說在組件掛載時將焦點設置到一個 input 元素上,或在一個元素上初始化一個第三方庫。

訪問模板引用

為了通過組合式 API 獲得該模板引用,我們需要聲明一個匹配模板 ref attribute 值的 ref:

<script setup>
import { ref, onMounted } from 'vue'

// 聲明一個 ref 來存放該元素的引用
// 必須和模板裡的 ref 同名
const input = ref(null)

onMounted(() => {
  input.value.focus()
})
</script>

<template>
  <input ref="input" />
</template>

如果不使用 script setup,需確保從 setup() 返回 ref:

export default {
  setup() {
    const input = ref(null)
    // ...
    return {
      input
    }
  }
}

注意,你只可以在組件掛載後才能訪問模板引用。如果你想在模板中的表達式上訪問 input,在初次渲染時會是 null,因為在初次渲染前這個元素還不存在!

如果你需要偵聽一個模板引用 ref 的變化,確保考慮到其值為 null 的情況:

watchEffect(() => {
  if (input.value) {
    input.value.focus()
  } else {
    // 此時還未掛載,或此元素已經被卸載(例如通過 v-if 控制)
  }
})

v-for 中的模板引用

需要 v3.2.25 及以上版本

當在 v-for 中使用模板引用時,對應的 ref 中包含的值是一個數組,它將在元素被掛載後包含對應整個列表的所有元素:

<script setup>
import { ref, onMounted } from 'vue'

const list = ref([
  /* ... */
])

const itemRefs = ref([])

onMounted(() => console.log(itemRefs.value))
</script>

<template>
  <ul>
    <li v-for="item in list" ref="itemRefs">
      {{ item }}
    </li>
  </ul>
</template>

應該注意的是,ref 數組並不保證與源數組相同的順序。

函數模板引用

除了使用字符串值作名字,ref attribute 還可以綁定為一個函數,會在每次組件更新時都被調用。該函數會收到元素引用作為其第一個參數:

<input :ref="(el) => { /* 將 el 賦值給一個數據屬性或 ref 變量 */ }">

注意我們這裡需要使用動態的 :ref 綁定才能夠傳入一個函數。當綁定的元素被卸載時,函數也會被調用一次,此時的 el 參數會是 null。你當然也可以綁定一個組件方法而不是內聯函數。

組件上的 ref

模板引用也可以被用在一個子組件上。這種情況下引用中獲得的值是組件實例:

<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'

const child = ref(null)

onMounted(() => {
  // child.value 是 <Child /> 組件的實例
})
</script>

<template>
  <Child ref="child" />
</template>

如果一個子組件使用的是選項式 API 或沒有使用 script setup,被引用的組件實例和該子組件的 this 完全一致,這意味著父組件對子組件的每一個屬性和方法都有完全的訪問權。這使得在父組件和子組件之間創建緊密耦合的實現細節變得很容易,當然也因此,應該只在絕對需要時才使用組件引用。大多數情況下,你應該首先使用標準的 props 和 emit 接口來實現父子組件交互。

有一個例外的情況,使用了 script setup 的組件是默認私有的:一個父組件無法訪問到一個使用了 script setup 的子組件中的任何東西,除非子組件在其中通過 defineExpose 宏顯式暴露:

<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

// 像 defineExpose 這樣的編譯器宏不需要導入
defineExpose({
  a,
  b
})
</script>

當父組件通過模板引用獲取到了該組件的實例時,得到的實例類型為 { a: number, b: number } (ref 都會自動解包,和一般的實例一樣)。


上一篇
vue3鍊成術第二十七天-偵聽器
下一篇
vue3鍊成術第二十九天-組件基礎(1)
系列文
自學vue~點亮Roadmap過程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言